/*
Copyright (C) 1996-1997 GX Media, Inc.
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "Qoole.h"
#include "Game.h"
#include "QTextureManager.h"
#include "QMainFrame.h"
#include "QDraw.h"

BEGIN_EVENT_TABLE(QTextureManager, wxDialog)
	EVT_CLOSE(QTextureManager::OnClose)
	EVT_BUTTON(wxID_OK, QTextureManager::OnOk)
	EVT_CHOICE(XRCID("gameChoice"), QTextureManager::OnGameChoice)
	EVT_LISTBOX(XRCID("textureArchiveSelection"), QTextureManager::OnArchiveSelection)
	EVT_LISTBOX(XRCID("textureArchive"), QTextureManager::OnArchiveTextureSelection)
	EVT_LISTBOX(XRCID("textureListSelection"), QTextureManager::OnListSelection)
	EVT_LISTBOX(XRCID("textureList"), QTextureManager::OnListTextureSelection)
	EVT_BUTTON(XRCID("addArchive"), QTextureManager::OnAddArchive)
	EVT_BUTTON(XRCID("removeArchive"), QTextureManager::OnRemoveArchive)
	EVT_BUTTON(XRCID("newList"), QTextureManager::OnNewList)
	EVT_BUTTON(XRCID("removeList"), QTextureManager::OnRemoveList)
	EVT_BUTTON(XRCID("add"), QTextureManager::OnAdd)
	EVT_BUTTON(XRCID("remove"), QTextureManager::OnRemove)
END_EVENT_TABLE()

QTextureManager::QTextureManager(wxWindow *parent)
{
	// Field initialization.
	texList = NULL;

	// Load the texture manager.
	QDraw::OutputText(_("Loading texture manager... "));
	wxXmlResource::Get()->LoadDialog(this, parent, wxT("QTextureManager"));

	// Load the controls.
	gameChoice = XRCCTRL(*this, "gameChoice", wxChoice);
	textureArchiveSelection = XRCCTRL(*this, "textureArchiveSelection", wxListBox);
	textureArchive = XRCCTRL(*this, "textureArchive", wxListBox);
	textureListSelection = XRCCTRL(*this, "textureListSelection", wxListBox);
	textureList = XRCCTRL(*this, "textureList", wxListBox);
	addArchive = XRCCTRL(*this, "addArchive", wxButton);
	removeArchive = XRCCTRL(*this, "removeArchive", wxButton);
	newList = XRCCTRL(*this, "newList", wxButton);
	removeList = XRCCTRL(*this, "removeList", wxButton);
	add = XRCCTRL(*this, "add", wxButton);
	remove = XRCCTRL(*this, "remove", wxButton);

	// Create the texture panel and his size.
	texturePanel = new QTexturePanel(this);
	wxXmlResource::Get()->AttachUnknownControl(wxT("texturePanel"), texturePanel, this);

	// Load the game name.
	IterLinkList<Game> it(Game::GetGames());
	int i;
	startGameIndex = 0;
	QooleDoc *document = GetMainFrame()->GetDeskTopDocument();
	if(!document)
		gameChoice->Append(wxT(""));

	for(i = 0, it.Reset(); !it.IsDone(); i++)
	{
		game = it.GetNext();
		gameChoice->Append(game->GetName());
		if(document != NULL & document->GetGame() == game)
			startGameIndex = i;
	}

	game = NULL;

	// Select current game.
	gameChoice->SetSelection(startGameIndex);
	wxCommandEvent ev;
	OnGameChoice(ev);

	QDraw::OutputText(_("OK.\n"));
}

QTextureManager::~QTextureManager()
{
}

void QTextureManager::OnClose(wxCloseEvent &ev)
{
	if(texList)
		texList->Save();
	ev.Skip();
}

void QTextureManager::OnOk(wxCommandEvent &ev)
{
	if(texList)
		texList->Save();
	ev.Skip();
}

void QTextureManager::OnGameChoice(wxCommandEvent &ev)
{
	addArchive->Enable(false);
	removeArchive->Enable(false);
	newList->Enable(false);
	removeList->Enable(false);
	add->Enable(false);
	remove->Enable(false);

	QooleDoc *document = GetMainFrame()->GetDeskTopDocument();

	int gameIndex = gameChoice->GetSelection();
	if(!document && gameIndex == 0)
		return;

	// Adjust the actual game index.
	if(!document)
		gameIndex--;

	// Get the game.
	game = &Game::GetGames()[gameIndex];
	if(!game->Init())
	{
		// Reset the game selection.
		game = NULL;

		gameChoice->SetSelection(startGameIndex);

		textureArchive->Clear();
		textureArchiveSelection->Clear();
		textureList->Clear();
		textureListSelection->Clear();

		SetTexInfo(wxEmptyString, 0);
		return;
	}

	// Enable according to the use of archives.
	bool usesWad = game->UsesWadFile();
	textureArchiveSelection->Enable(usesWad);
	removeArchive->Enable(usesWad);

	addArchive->Enable(true);
	newList->Enable(true);
	BuildArchiveList();

	// Load texture lists.
	textureListSelection->Clear();

	QDraw::OutputText(_("Loading texture lists... "));
	wxString searchDir = wxString::Format(wxT("%s/texlists/%s"), LFile::GetInitDir().c_str(),
				game->GetName().c_str());
	wxString texFile;
	LFindFiles findTexFiles(searchDir, wxT("*.tex"));
	int i = 0;
	for(texFile = findTexFiles.Next(); !texFile.empty(); texFile = findTexFiles.Next())
	{
		wxFileName texName = texFile;
		textureListSelection->Append(texName.GetName());
	}
	QDraw::OutputText(_("OK.\n"));

	textureArchive->Clear();
	textureList->Clear();

	SetTexInfo(wxEmptyString, 0);

	textureArchiveSelection->SetSelection(0);
	//OnArchiveSelection(ev);

	textureListSelection->SetSelection(0);
	OnListSelection(ev);
}

void QTextureManager::OnArchiveSelection(wxCommandEvent &ev)
{
	// Ignore if there isn't a game.
	if(!game)
		return;

	// Avoid calling befor of time.
	int selected = textureArchiveSelection->GetSelection();
	if(selected == -1)
		return;

	textureArchive->Clear();
	TexDir *texDir = game->GetTexDB()->GetTexDir(selected);
	if(!texDir)
		return;

	texDirName = texDir->dirName;
	if(!game->UsesWadFile())
	{
		wxString dirName, texName;
		LFindDirs finddirs(texDirName);
		while(!(dirName = finddirs.Next()).empty())
		{
			wxString newDir;
			newDir.Printf(wxT("%s/%s"),	texDirName.c_str(), dirName.c_str());

			LFindFiles findfiles(newDir);
			while(!(texName = findfiles.Next()).empty())
			{
				wxString filename, realname;
				filename.Printf(wxT("%s/%s"), newDir.c_str(), texName.c_str());

				wxFileName name = texName;
				realname.Printf(wxT("%s/%s"), dirName.c_str(), name.GetName().c_str());

				if(!(texName[0] == wxT('+') && texName[1] > wxT('0')))
				{
					textureArchive->Append(realname, (void*)0);
				}
			}
		}
	}
	else
	{
		WadHeader wadHeader;
		WadEntry *wadEntry;

		LFile file;
		if(!file.Open(texDirName))
			return;

		file.Read(&wadHeader, sizeof(WadHeader));
		if(strncmp(wadHeader.magic, "WAD2", 4) &&
			strncmp(wadHeader.magic, "WAD3", 4))
			return;

		wadEntry = new WadEntry[wadHeader.entries];
		file.Seek(wadHeader.offset);
		file.Read(wadEntry, sizeof(WadEntry), wadHeader.entries);

		for(int i = 0; i < wadHeader.entries; i++)
		{
			char *texName = wadEntry[i].name;
			if(!(texName[0] == '+' && texName[1] > '0') && strlen(texName))
				textureArchive->Append(wxString(texName, wxConvUTF8), (void*)wadEntry[i].offset);
		}

		delete [] wadEntry;
	}

	textureList->Clear();
	for(int i = 0; i < texList->GetNumTexs(); i++)
	{
		Texture *texture = texList->GetTexNum(i);
		const wxString &name = texture->GetName();
		if(name.empty())
			continue;

		int num = textureList->Append(name);
		if(game->UsesWadFile())
			textureList->SetClientData(num, (void*)texture->texEntry->wadEntry.offset);
		else
			textureList->SetClientData(num, (void*)0);

		int index = textureArchive->FindString(name, true);
		if(index != wxNOT_FOUND)
			textureArchive->Delete(index);
	}

	add->Enable(false);
	remove->Enable(false);
	removeArchive->Enable(true);
}

void QTextureManager::OnListSelection(wxCommandEvent &ev)
{
	// Ignore if there isn't a game.
	if(!game)
		return;

	const wxString &listName = textureListSelection->GetStringSelection();
	wxString fullname;
	fullname.Printf(wxT("%s/texlists/%s/%s.tex"),
			LFile::GetInitDir().c_str(), game->GetName().c_str(), listName.c_str());

	if(texList)
	{
		texList->Save();
		delete texList;
	}
	texList = new TexList(fullname, game);

	OnArchiveSelection(ev);
	removeList->Enable(true);
}

void QTextureManager::OnArchiveTextureSelection(wxCommandEvent &ev)
{
	// Ignore if there isn't a game.
	if(!game)
		return;

	// Avoid unexpected events.
	wxArrayInt selections;
	textureArchive->GetSelections(selections);
	if(selections.Count() == 0)
		return;

	// Only display the last.
	int selection = selections.Item(selections.Count() - 1);
	const wxString &texName = textureArchive->GetString(selection);
	SetTexInfo(texName, (size_t)textureArchive->GetClientData(selection));
	textureList->SetSelection(-1, false);

	add->Enable(true);
	remove->Enable(false);
}

void QTextureManager::OnListTextureSelection(wxCommandEvent &ev)
{
	// Ignore if there isn't a game.
	if(!game)
		return;

	// Avoid unexpected events.
	wxArrayInt selections;
	textureList->GetSelections(selections);
	if(selections.Count() == 0)
		return;

	// Only display the first.
	int selection = selections.Item(selections.Count() - 1);
	const wxString &texName = textureList->GetString(selection);
	SetTexInfo(texName, (size_t)textureList->GetClientData(selection));
	textureArchive->SetSelection(-1, false);

	add->Enable(false);
	remove->Enable(true);
}

void QTextureManager::OnAddArchive(wxCommandEvent &ev)
{
	if(!game)
		return;

	// Compute the default path.
	wxString defaultPath;
	if(game->UsesWadFile())
		defaultPath = wxPathOnly(game->GetTexDB()->GetTexDir(0)->dirName);
	else
		defaultPath = game->GetGameDir();

	// Ask for the archive.
	wxString filename =
			wxFileSelector(_("Select texture archive."),
					defaultPath, wxEmptyString, wxT("*.wad"),
					game->UsesWadFile() ? _("WAD files (*.wad)|*.wad") : _("Pak Files (*.pak)|*.pak"),
					0, GetMainFrame());
	if(filename.empty())
		return;

	if(game->UsesWadFile())
	{
		game->GetTexDB()->AddTexDir(filename);
		BuildArchiveList();
	}
	else
	{
		LPak *pak = LFile::UsePak(filename);
		ExtractTexsFromPak(pak, game->GetBaseDir());
		OnArchiveSelection(ev);
	}
}

void QTextureManager::OnRemoveArchive(wxCommandEvent &ev)
{
	int selection = textureArchiveSelection->GetSelection();
	if(selection < 0)
		return;

	if(!game)
		return;

	if(wxMessageBox(_("Are you sure?"), _("Remove Archive"), wxOK|wxCANCEL, this) == wxCANCEL)
		return;

	TexDir *texDir = game->GetTexDB()->GetTexDir(selection);
	game->GetTexDB()->DelTexDir(texDir);
	removeArchive->Enable(false);
}

void QTextureManager::OnNewList(wxCommandEvent &ev)
{
	if(!game)
		return;

	wxString listName = wxGetTextFromUser(_("List name."), _("New Texture List"));
	if(listName.empty())
		return;

	wxString fullName;
	fullName.Printf(wxT("%s/texlists/%s/%s.tex"), LFile::GetInitDir().c_str(),
				game->GetName().c_str(), listName.c_str());

	if(LFile::Exist(fullName))
		return;

	int id = textureListSelection->Append(listName);
	textureListSelection->SetSelection(id);
	OnListSelection(ev);
}

void QTextureManager::OnRemoveList(wxCommandEvent &ev)
{
	int selection = textureListSelection->GetSelection();
	if(selection < 0)
		return;

	if(!game)
		return;

	if(wxMessageBox(_("Are you sure?"), _("Remove List"), wxOK|wxCANCEL, this) == wxCANCEL)
		return;

	// Compute the list file name.
	wxString listName = textureListSelection->GetString(selection);
	wxString fullName;
	fullName.Printf(wxT("%s/texlists/%s/%s.tex"), LFile::GetInitDir().c_str(),
			game->GetName().c_str(), listName.c_str());

	// Remove the list.
	textureListSelection->Delete(selection);
	OnListSelection(ev);

	// Delete the list file.
	wxRemoveFile(fullName);

	removeList->Enable(false);
}

struct TextureData
{
	wxString name;
	unsigned int offset;
};

void QTextureManager::OnAdd(wxCommandEvent &ev)
{
	// Get the selections indices.
	wxArrayInt selections;
	textureArchive->GetSelections(selections);
	if(selections.Count() == 0)
		return;

	// Read the selections.
	std::vector<TextureData> selectedTextures;
	for(size_t i = 0; i < selections.Count(); i++)
	{
		int index = selections.Item(i);
		TextureData data;
		data.name = textureArchive->GetString(index);
		data.offset = (size_t)textureArchive->GetClientData(index);
		selectedTextures.push_back(data);
	}

	// Add the selected textures.
	for(size_t i = 0; i < selectedTextures.size(); i++)
	{
		const TextureData &data = selectedTextures[i];
		if(textureList->FindString(data.name, true) == wxNOT_FOUND)
		{
			wxString addName = data.name;

			if(game->UsesWadFile())
			{
				TexDir *texDir = game->GetTexDB()->GetTexDir(textureArchiveSelection->GetSelection());
				if(texDir)
					addName.Printf(wxT("%s/%s"), texDir->dirName.c_str(), data.name.c_str());
			}

			texList->Add(addName);
			textureList->Append(data.name, (void*)data.offset);

			int oldIndex = textureArchive->FindString(data.name, true);
			if(oldIndex != wxNOT_FOUND)
				textureArchive->Delete(oldIndex);
		}
	}
}

void QTextureManager::OnRemove(wxCommandEvent &ev)
{
	if(!texList)
		return;

	// Get the selections indices.
	wxArrayInt selections;
	textureList->GetSelections(selections);
	if(selections.Count() == 0)
		return;

	// Read the selections.
	std::vector<TextureData> selectedTextures;
	for(size_t i = 0; i < selections.Count(); i++)
	{
		int index = selections.Item(i);
		TextureData data;
		data.name = textureList->GetString(index);
		data.offset = (size_t)textureList->GetClientData(index);
		selectedTextures.push_back(data);
	}

	// Remove the selected textures.
	for(size_t i = 0; i < selectedTextures.size(); i++)
	{
		const TextureData &data = selectedTextures[i];
		int index = textureList->FindString(data.name, true);
		if(index != wxNOT_FOUND)
		{
			textureList->Delete(index);
			textureArchive->Append(data.name, (void*)data.offset);
			texList->Remove(data.name);
		}
	}
}

void QTextureManager::BuildArchiveList()
{
	if(!game)
		return;

	// Clear the archive/dir selection list.
	textureArchiveSelection->Clear();

	// Append the dirs.
	TexDB *db = game->GetTexDB();
	int dirs = db->GetNumTexDirs();
	for(int i = 0; i < dirs; i++)
	{
		TexDir *dir = db->GetTexDir(i);
		wxString dirName = dir->dirName.AfterLast('/');
		textureArchiveSelection->Append(dirName);
	}
}

void QTextureManager::SetTexInfo(const wxString &texName, int offset)
{
	// Delete the previous texture.
	Texture *texture = texturePanel->GetTexture();
	if(texture)
	{
		// Use first an empty texture.
		texturePanel->SetTexture(NULL);
		delete texture;
	}

	// Set empty the empty texture.
	if(texName.empty())
		return;

	wxString filename;
	filename.Printf(wxT("%s/%s%s"), texDirName.c_str(), texName.c_str(), game->GetTexExt().c_str());

	// Create the texture entry.
	texEntry = TexEntry();
	texEntry.name = texName;
	texEntry.texDir = textureArchiveSelection->GetSelection();
	texEntry.wadEntry.offset = offset;
	strncpy(texEntry.wadEntry.name, texName.utf8_str(), 16);

	// Load the texture.
	texture = new Texture(&texEntry, game);
	texturePanel->SetTexture(texture);
}

